package com.wing.gateway.config;

import cn.hutool.core.util.StrUtil;
import com.wing.common.constant.GlobalConstants;
import com.wing.common.constant.SecurityConstants;
import com.wing.common.utils.RSAUtils;
import com.wing.redis.utils.redis.RedisHelperImpl;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 网关自定义鉴权管理器
 *
 * @author
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class ResourceServerManager implements ReactiveAuthorizationManager<AuthorizationContext> {

    private final RedisHelperImpl redisHelper;

    @SneakyThrows
    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
        ServerHttpRequest request = authorizationContext.getExchange().getRequest();
        // 预检请求放行
        if (request.getMethod() == HttpMethod.OPTIONS) {
            return Mono.just(new AuthorizationDecision(true));
        }
        PathMatcher pathMatcher = new AntPathMatcher();
        String method = request.getMethodValue().toLowerCase();
        String path = request.getURI().getPath();
        // RESTFul接口权限设计
        String restfulPath = method + ":" + path;

        // 如果token以"bearer "为前缀，到此方法里说明JWT有效即已认证，其他前缀的token则拦截
        String token = request.getHeaders().getFirst(SecurityConstants.AUTHORIZATION_KEY);
        String [] urls = {
            "/file/admin/newFile/downloadFile",
            "/user/admin/department/export",
            "/user/admin/role/export",
            "/user/admin/user/export"
        };
        List<String> lists = Arrays.asList(urls);
        if(StrUtil.isNotBlank(path) && lists.indexOf(path) >= 0){
            // 下载的请求验证token
            token = request.getQueryParams().getFirst("access_token");
            // RSA私钥解码
            // 取前172位解码
            if(token.length() > 172){
                String tokenLeft = token.substring(0, 172);
                String tokenright = token.substring(172);
                String decryptTokenLeft = RSAUtils.decryptByPrivateKey(RSAUtils.privateKey, tokenLeft);
                token = decryptTokenLeft + tokenright;
            }
        }
        if (StrUtil.isNotBlank(token) && StrUtil.startWithIgnoreCase(token, SecurityConstants.JWT_PREFIX) ) {
            if (pathMatcher.match(SecurityConstants.APP_API_PATTERN, path)) {
                // 移动端请求只需认证，无需后续鉴权
                return Mono.just(new AuthorizationDecision(true));
            }
        } else {
            return Mono.just(new AuthorizationDecision(false));
        }

        /**
         * 鉴权开始
         *
         * 缓存取 [URL权限-角色ID集合] 规则数据
         */

        // 是否需要鉴权，默认未设置拦截规则不需鉴权
        if (!redisHelper.hasKey(GlobalConstants.URL_AUTH_ROLES_KEY + restfulPath)) {
            return Mono.just(new AuthorizationDecision(true));
        }

        // 根据请求路径获取角色ID列表
        List<String> roleIdList = (ArrayList<String>) redisHelper.getValue(GlobalConstants.URL_AUTH_ROLES_KEY + restfulPath);
        // 判断JWT中携带的用户角色ID是否有权限访问
        Mono<AuthorizationDecision> authorizationDecisionMono = mono
                .filter(Authentication::isAuthenticated)
                .flatMapIterable(Authentication::getAuthorities)
                .map(GrantedAuthority::getAuthority)
                .any(authority -> {

                    // 用户的角色ID
                    String roleId = authority.substring(SecurityConstants.AUTHORITY_PREFIX.length());

                    // 如果是超级管理员则放行
                    // if (GlobalConstants.ROOT_ROLE_CODE.equals(roleCode)) {
                    //     return true;
                    // }

//                    return roleIdList.contains(roleId);
                    return true;
                })
                .map(AuthorizationDecision::new)
                .defaultIfEmpty(new AuthorizationDecision(true));

        return authorizationDecisionMono;
    }

    public static void main(String[] args) throws Exception {
        String ss = "iNjCYQz0k3n6RfYsqOGSBgV0VQRNF+ZO0yXpMWjW2gc9SkVYA5eO9U23DITNdlNrIAZxSlZrkvtZ3C4SJR7RVeFHM5vAey8om7Aesv8+3ENrrlxO3bbkwOxaZVxNAA4zQo5ffvQSvsaxgz0Yi3voT89xFYPXVcT1jxWzykUJDZU=";
        System.out.println(ss);
        String decryptTokenLeft = RSAUtils.decryptByPrivateKey(RSAUtils.privateKey, ss);
        System.out.println(decryptTokenLeft);
    }

}
